package proxy.gui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

import proxy.Configure;
import proxy.ConnectionDetail;
import proxy.HttpSession;
import proxy.Log;

public class HttpProxy extends JFrame implements Runnable{

	private static final long serialVersionUID = 5990915277693744531L;
	private static final String[] columns = new String[]{"No.", "Local Address & Port", "Remote Address & Port", "Resource"};
	private ServerSocket server = null;
	JMenuBar menuBar;
	JMenu menuFile;
	JMenu menuConfigure;
	JTable table;
	DefaultTableModel tableModel;
	List<ConnectionDetail> connections;
	Configure configure = Configure.getInstance();
	DialogConfigure dialogConfigure;
	Queue<Thread> threads = new ArrayDeque<Thread>();
	Thread update;
	boolean stopped = false;
	
	public HttpProxy(){
		super();
		setTitle("WWW Proxy");
		setSize(800, 600);
		setVisible(true);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e){
				Configure.getInstance().save();
			}
		});
		
		update = new Thread(){
			public void run(){
				while (true){
					while (!threads.isEmpty()) {
						final Thread thread = threads.poll();
						if (thread == null){
							break;
						}else{
							thread.start();
							try {
								thread.join();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
					}
					synchronized (update) {
						try {
							update.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		};
		update.start();
		connections = new ArrayList<ConnectionDetail>();
		table = new JTable(){
			private static final long serialVersionUID = 6675984150130053634L;

			@Override
			public boolean isCellEditable(int row, int column) {
				return false;
			}
			
			@Override
			public TableCellRenderer getCellRenderer(int row, int column) {
				return new ConnectionRenderer();
			}
		};
		table.setModel(tableModel = new DefaultTableModel(new String[][]{}, columns));
		table.setRowHeight(25);
		table.setFocusable(false);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
		for (int i = 0; i < configure.columnWidth.length; i++){
			if (configure.columnWidth[i] == 0) continue;
			table.getColumnModel().getColumn(i).setMaxWidth(configure.columnWidth[i]);
			table.getColumnModel().getColumn(i).setMinWidth(configure.columnWidth[i]);
		}
		table.setFont(table.getFont().deriveFont(12f));
		table.setFillsViewportHeight(true);
		initMenuBar();
		
		Container container = getContentPane();
		container.setLayout(new BorderLayout());
		container.add(new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER);
		new Thread(this).start();
	}
	
	private void initMenuBar(){
		menuBar = new JMenuBar();
		menuFile = new JMenu("File");
		JMenuItem itemExit = new JMenuItem("Exit");
		itemExit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Configure.getInstance().save();
				System.exit(0);
			}
		});
		menuFile.add(itemExit);
		
		menuConfigure = new JMenu("Tools");
		JMenuItem itemConfigureDialog = new JMenuItem("Configure...");
		itemConfigureDialog.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (dialogConfigure == null) dialogConfigure = new DialogConfigure();
				dialogConfigure.setLocationRelativeTo(HttpProxy.this);
				dialogConfigure.setVisible(true);
				dialogConfigure = null;
			}
		});
		menuConfigure.add(itemConfigureDialog);
		
		menuBar.add(menuFile);
		menuBar.add(menuConfigure);
		setJMenuBar(menuBar);
	}
	
	public void addConnection(final ConnectionDetail connection){
		threads.add(new Thread(){
			public void run(){
				//System.out.println(connection);
				connections.add(connection);
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						tableModel.addRow(connection.getTableValues());
					}
				});
			}
		});
		synchronized (update) {
			update.notifyAll();
		}
	}
	
	public void removeConnection(final ConnectionDetail connection){
		threads.add(new Thread() {
			public void run() {
				//System.out.println(connection);
				final int index = connections.indexOf(connection);
				if (index <= -1) return;
				connections.remove(index);
				SwingUtilities.invokeLater(new Runnable() {
					@Override
					public void run() {
						tableModel.removeRow(index);
					}
				});
			}
		});
		synchronized (update) {
			update.notifyAll();
		}
	}
	
	@Override
	public void run() {
		try {
			server = new ServerSocket(configure.port, 0, InetAddress.getByName(configure.host));
			Log log = Log.getLog("debug.txt");
			while (!stopped) {				
				new HttpSession(server.accept(), HttpProxy.this, log).start();
			}
		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(this, "Can't start proxy server. Please configuare againt host and port of proxy server.\nThen restart this application");
		}
		if (server != null)
			try {
				server.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}
	
	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				try {
					UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
				} catch (Exception e) {
					e.printStackTrace();
				}
				new HttpProxy();
			}
		});
	}
}
